package LAAMI;

import java.util.*;
import repast.simphony.context.Context;
import repast.simphony.dataLoader.ContextBuilder;
import repast.simphony.engine.environment.RunEnvironment;
import repast.simphony.parameter.*;

public class LAAMIBuilder implements ContextBuilder<Object> {

	// Limits for patience and judgement
	private static int PATIENCE_MIN = 5;
	private static int PATIENCE_MAX = 45;
	private static double JUDGEMENT_MIN = 0.5;
	private static double JUDGEMENT_MAX = 1.5;
	// Instance of OptionMarket kept by LAAMIBuilder
	private static OptionMarket optionmarket;
	// Instance of StockMarket kept by LAAMIBuilder
	private static StockMarket stockmarket;
	// ArrayList of all the Investor instances
	private static ArrayList<Investor> agentList;
	private static double initialStockPrice;
	private static double initialOptionPrice = 0;
	// Drift of the stock market prices
	private static double drift;
	// Volatility of the stock market prices
	private static double stockVolatility;
	// Number of agents (investors)
	private static int investorCount;
	// End simulation at simulationEnd day
	private static int simulationEnd=365;
	private static int expDay;
	// Maturity: Time to option expiration in years
	// Values of maturity for experiments: 0.25 to 1.25
	private static double maturity;
	// Risk-free interest rate
	private static double interestRate = 0.03;

	@Override
	public Context build(Context<Object> context) {
		context.setId("LAAMI");

		// Change the parameters' values to the ones given by the user
		setUserVals();

		// The expiration day is equal to the maturity multiplied by the
		// number of trading days in a year plus the 50 days that correspond
		// to the first stage of the experiment
		expDay=(int) (maturity*252+50);
		
		// End simulation instance at "simulationEnd" day
		RunEnvironment.getInstance().endAt(simulationEnd);

		// Build a StockMarket. Put it in the context.
		stockmarket = new StockMarket(initialStockPrice, stockVolatility, drift);
		context.add(stockmarket);

		// Build an OptionMarket. Put it in the context.
		optionmarket = new OptionMarket(initialOptionPrice, maturity);
		context.add(optionmarket);

		agentList = new ArrayList<Investor>();

		// Build investorCount Investors. Put them into context and AgentList.
		for (int i = 0; i < investorCount; i++) {
			Random g = new Random();
			// Random values for patience and judgment
			int patience = g.nextInt(40) + 5;
			double judgement = g.nextDouble() + 0.5;
			// Initialize Investor instances
			Investor investor = new Investor(patience, judgement);
			// Add it to the context
			context.add(investor);
			// Add it to the agentList
			agentList.add(investor);
		}

		return context;
	}

	// In this method (only for single runs) we take the parameters given
	// from the user in the "LAAMI.rs/parameters.xml" file and we put it
	// in the ContextBuilder. In that way, user can manage the simulation
	// variables from outside. When this simulation is run from the
	// Repast framework, a graphic user interface (GUI) is presented.
	// The user can define the value of the internal parameters from there.
	// Those valuse are set in the begining of each individual run and
	// cannot be changed after the simulation has started.
	// The parameters that can be modified from the GUI (or the xml file)
	// are: investorCount, initialStockPrice, drift, stockVolatility,
	// interestRate and maturity.
	public void setUserVals() {
		Parameters userVals = RunEnvironment.getInstance().getParameters();
		investorCount = (Integer) userVals.getValue("investorCount");
		initialStockPrice = (Float) userVals.getValue("initialStockPrice");
		setDrift((Float) userVals.getValue("drift"));
		double s = (Float) userVals.getValue("stockVolatility");
		if (s < 0) {
			System.out.println("Incorrect value for stock Volatility.");
			return;
		}
		setStockVolatility(s);
		double i = (Float) userVals.getValue("interestRate");
		if (i < 0) {
			System.out.println("Incorrect value for interest Rate.");
			return;
		}
		setInterestRate(i);
		double m = (Float) userVals.getValue("maturity");
		if (m < 0.25 || m > 1.25) {
			System.out.println("Incorrect value for maturity.");
			return;
		}
		setMaturity(m);
	}

	/* -------------setters/getters-------------------- */
	public static OptionMarket getOptionmarket() {
		return optionmarket;
	}

	public static StockMarket getStockmarket() {
		return stockmarket;
	}

	public static ArrayList<Investor> getAgentList() {
		return agentList;
	}

	public void setAgentList(ArrayList<Investor> a) {
		agentList = a;
	}

	public static int getPATIENCE_MIN() {
		return PATIENCE_MIN;
	}

	public static void setPATIENCE_MIN(int pATIENCE_MIN) {
		PATIENCE_MIN = pATIENCE_MIN;
	}

	public static int getPATIENCE_MAX() {
		return PATIENCE_MAX;
	}

	public static void setPATIENCE_MAX(int pATIENCE_MAX) {
		PATIENCE_MAX = pATIENCE_MAX;
	}

	@Parameter(displayName = "Initial Stock Price", usageName = "initialStockPrice", defaultValue = "100", converter = "repast.simphony.parameter.StringConverterFactory$FloatConverter")
	public static double getInitialStockPrice() {
		return initialStockPrice;
	}

	public static void setInitialStockPrice(double i) {
		initialStockPrice = i;
	}

	@Parameter(displayName = "Drift", usageName = "drift", defaultValue = "0.0", converter = "repast.simphony.parameter.StringConverterFactory$FloatConverter")
	public static double getDrift() {
		return drift;
	}

	public static void setDrift(double d) {
		drift = d;
	}

	@Parameter(displayName = "Stock Volatility", usageName = "stockVolatility", defaultValue = "0.3", converter = "repast.simphony.parameter.StringConverterFactory$FloatConverter")
	public static double getStockVolatility() {
		return stockVolatility;
	}

	public static void setStockVolatility(double s) {
		stockVolatility = s;
	}

	@Parameter(displayName = "Investor Count", usageName = "investorCount", defaultValue = "50", converter = "repast.simphony.parameter.StringConverterFactory$IntConverter")
	public static int getInvestorCount() {
		return investorCount;
	}

	public static void setInvestorCount(int i) {
		investorCount = i;
	}

	public static double getMaturity() {
		return maturity;
	}

	public static void setMaturity(double maturity) {
		LAAMIBuilder.maturity = maturity;
	}

	public static double getInterestRate() {
		return interestRate;
	}

	public static void setInterestRate(double interestRate) {
		LAAMIBuilder.interestRate = interestRate;
	}

	public static int getExpDay() {
		return expDay;
	}

}
